package rolesystem.examples.jade.auction.seller;

import rolesystem.core.*;
import rolesystem.roles.auction.*;
import rolesystem.jade.agents.*;

import java.io.Serializable;

/**
 * This class represents a possible behaviour of a seller.
 * This is a subject layer that can be used by agents in a role system.
 */
public class SellerSL implements SubjectLayer
{
	/**
	 * Time in milliseconds to wait between searches.
	 */
	private static final long PAUSE=2000;

	/**
	 * Maximum time to wait for replies.
	 */
	private static final long REPTO=3000;

	/**
	 * Reserve price.
	 */
	private Price reserve;

	/**
	 * Description of good to put on sale.
	 */
	private String goodDesc;

	/**
	 * Good to put on sale.
	 */
	private Serializable good;

	/**
	 * Gained money.
	 */
	private Price gain;


	/**
	 * @param reserve Reserve price.
	 * @param goodDesc Description of good to put on sale.
	 * @param good Good to put on sale.
	 */
	public SellerSL(Price reserve, String goodDesc,
		Serializable good)
	{
		this.reserve=reserve;
		this.goodDesc=goodDesc;
		this.good=good;
	}
	
	
	public void go(RoleSystem roleSystem)
	{
		RoleRegistration registration;
		int[] auctioneersArray;
		int auctioneer;
		int count;
		RoleEvent event;
		boolean again1;
		boolean again2;
		boolean again3;
		long residualTime, finalTime;

		try
		{
			registration=roleSystem.reqRegistration(Seller.ROLE_ID);
		}
		catch(RoleException re)
		{
			System.err.println(re);
			return;
		}
		try
		{
			// Loop until the good is sold.
			again1=true;
			do
			{
				// Loop until an available auctioneer is found.
				again2=true;
				do
				{
					try
					{
						Thread.sleep(PAUSE);
					}
					catch(InterruptedException ie)
					{
					}
					auctioneersArray=roleSystem.searchForRole(Auctioneer.ROLE_ID);
					count=(int)(Math.random()*auctioneersArray.length);
					auctioneer=auctioneersArray[count];
					try
					{
						registration.doAction(Seller.
							reqSale(auctioneer));
					}
					catch(RoleException re)
					{
						if(!RoleException.NOT_VALID_ADD.equals(re.getMessage()))
							continue;
						else
							throw re;
					}
					// Loop until a suitable reply is received.
					again3=true;
					finalTime=System.currentTimeMillis()+REPTO;
					do
					{
						residualTime=finalTime-System.currentTimeMillis();
						if(residualTime<=0)
							// Timeout elapsed before the auctioneer has replied.
							break;
						event=registration.listen(residualTime);
						if(event==null)
							continue;
						if(event.getSender()==auctioneer)
						{
							// A do...while(false) block is used, so a break
							// causes the exit from this block.
							do
							{
								if(Seller.KE_saleRefused.match(event))
								{
									again3=false;
									break;
								}
								if(Seller.KE_saleAccepted.match(event))
								{
									again2=false;
									again3=false;
									break;
								}
								// Other events are ignored.
							} while(false);
						}
					} while(again3);
					// A suitable reply is received.
				} while(again2);
				// An available auctioneer is found.

				registration.doAction(Seller.
					putOnSale(auctioneer, new Sale(
					reserve, goodDesc, good)));
				// Loop until a suitable reply is received.
				again2=true;
				do
				{
					event=registration.listen();
					// A do...while(false) block is used, so a break
					// causes the exit from this block.
					do
					{
						if(Seller.KE_pay.match(event))
						{
							again1=false;
							again2=false;
							break;
						}
						if(Seller.KE_unsold.match(event))
						{
							again2=false;
							break;
						}
						// Other events are ignored.
					} while(false);
				} while(again2);
				// A suitable reply is received.
			} while(again1);
			// The good is sold.
			gain=((Money)event.getContent()).getAmount();
		}
		catch(RoleException re)
		{
			System.err.println(re);
		}
		try
		{
			registration.dismiss();
		}
		catch(RoleException re)
		{
			System.err.println(re);
		}
	}


	/**
	 * Returns the gained money.
	 * @return the gained money.
	 */
	public Price getMoney()
	{
		return gain;
	}
}
